﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;

namespace Demos.TravelingSalesPerson
{
    internal class ViewModel : INotifyPropertyChanged
    {
        private const int RadioVertice = 15;
        private readonly Dictionary<ElementoVisual, int> _aristasVisuales;
        private readonly Dictionary<ElementoVisual, int> _verticesVisuales;
        private Model _model;
        public bool _nuevoVertice;

        private ObservableCollection<ElementoVisual> _arbol;
        public ObservableCollection<ElementoVisual> Arbol
        {
            get { return _arbol; }
            set
            {
                _arbol = value;
                NotifyPropertyChanged("Arbol");
            }
        }

        private int _numeroVertices;
        public string NumeroVertices
        {
            get { return _numeroVertices.ToString(); }
            set
            {
                try
                {
                    _numeroVertices = int.Parse(value);
                    NotifyPropertyChanged("NumeroVertices");
                }
                catch (FormatException)
                {
                    MessageBox.Show("Error de formato", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private int _maxAristas;
        public string MaximasAristas
        {
            get { return _maxAristas.ToString(); }
            set
            {
                try
                {
                    _maxAristas = int.Parse(value);
                    NotifyPropertyChanged("MaximasAristas");
                }
                catch (FormatException)
                {
                    MessageBox.Show("Error de formato", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        public string ContenidoBoton { get; set; }

        public double MaxX { get; set; }
        public double MaxY { get; set; }

        private double _x;
        public double X
        {
            get { return _x; }
            set
            {
                _x = value;
                NotifyPropertyChanged("X");
            }
        }

        private double _y;
        public double Y
        {
            get { return _y; }
            set
            {
                _y = value;
                NotifyPropertyChanged("Y");
            }
        }

        private string _verticeSeleccionado;
        public string VerticeSeleccionado
        {
            get { return _verticeSeleccionado; }
            set
            {
                _verticeSeleccionado = value;
                NotifyPropertyChanged("VerticeSeleccionado");
            }
        }

        private string _aristaSeleccionado;
        public string AristaSeleccionado
        {
            get { return _aristaSeleccionado; }
            set
            {
                _aristaSeleccionado = value;
                NotifyPropertyChanged("AristaSeleccionado");
            }
        }

        private bool[] _seleccionAlgoritmos;
        public bool[] SeleccionAlgoritmos
        {
            get { return _seleccionAlgoritmos; }
            set
            {
                _seleccionAlgoritmos = value;
                NotifyPropertyChanged("SeleccionAlgoritmos");
            }
        }

        private string[] _tiempos;
        public string[] Tiempos
        {
            get { return _tiempos; }
            set
            {
                _tiempos = value;
                NotifyPropertyChanged("Tiempos");
            }
        }

        public ICommand CrearArbolAleatorioCommand { get; private set; }
        public ICommand AñadirVerticesCommand { get; private set; }
        public ICommand AñadirVerticeCommand { get; private set; }
        public ICommand OcultarPesosCommand { get; private set; }
        public ICommand BorrarElementoCommand { get; private set; }
        public ICommand CrearNuevaAristaCommand { get; private set; }
        public ICommand BorrarArbolCommand { get; private set; }
        public ICommand SeleccionarVerticeCommand { get; private set; }
        public ICommand SeleccionarAristaCommand { get; private set; }
        public ICommand BorrarSeleccionCommand { get; private set; }

        public ICommand VerSolucionCommand { get; private set; }
        public ICommand ResolverCommand { get; private set; }
        public ICommand AvanzarCommand { get; private set; }
        public ICommand PararCommand { get; private set; }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private List<Tuple<Vertice, Vertice>> _solucion;

        private Algoritmia.Problems.TravelingSalesPerson.ITravelingSalesPerson _solver;


        public ViewModel(Algoritmia.Problems.TravelingSalesPerson.ITravelingSalesPerson solver)
        {
            _solver = solver;
            Arbol = new ObservableCollection<ElementoVisual>();
            _solucion = new List<Tuple<Vertice, Vertice>>();
            _verticesVisuales = new Dictionary<ElementoVisual, int>();
            _aristasVisuales = new Dictionary<ElementoVisual, int>();
            _model = new Model(0, 0, 0, 0, RadioVertice, RadioVertice);
            NumeroVertices = "3";
            MaximasAristas = "1";
            _model.NuevoElemento += CrearElemento;
            SeleccionAlgoritmos = new bool[3];
            SeleccionAlgoritmos[0] = true;
            Tiempos = new string[3];

            MaxX = 300;
            MaxY = 200;

            CrearArbolAleatorioCommand = new RelayCommand(CrearArbol, () => Arbol.Count == 0);
            AñadirVerticesCommand = new RelayCommand(AñadirVertices, () => true);
            AñadirVerticeCommand = new RelayCommand(AñadirVertice, () => _nuevoVertice);
            OcultarPesosCommand = new RelayCommand(OcultarPesos, 
                                  () => Arbol.Any(elementoVisual => elementoVisual is AristaVisual && elementoVisual.Etiqueta != ""));
            BorrarElementoCommand = new RelayCommand(EliminarElemento, () => _model.AristaSeleccionada >= 0
                                                                             || _model.Origen != null 
                                                                             || _model.Destino != null);
            CrearNuevaAristaCommand = new RelayCommand(AñadirArista,
                                                       () => _model.Origen != null && _model.Destino != null);
            BorrarArbolCommand = new RelayCommand(BorrarArbol, () => Arbol.Count > 0);
            SeleccionarVerticeCommand = new RelayCommand(SeleccionarVertice, () => !_nuevoVertice);
            SeleccionarAristaCommand = new RelayCommand(SeleccionarArista, () => Arbol.Count > 0);
            BorrarSeleccionCommand = new RelayCommand(ResetSeleccion, 
                                                      () => _model.Origen != null || _model.Destino != null
                                                            || _model.AristaSeleccionada >= 0);

            VerSolucionCommand = new RelayCommand(Solucionar, () => Arbol.Count > 0);
            ResolverCommand = new RelayCommand(Resolver, () => Arbol.Count > 0 && _solucion.Count == 0);
            AvanzarCommand = new RelayCommand(Step, () => _solucion.Count > 0);
            PararCommand = new RelayCommand(() => _solucion.Clear(), () => _solucion.Count > 0);

            _nuevoVertice = false;
            ContenidoBoton = "Añadir vértices";
            NotifyPropertyChanged("ContenidoBoton");



            //Arbol prefabricado
            _model = new Model(0, 0, 0, 0, RadioVertice, RadioVertice);
            _model.NuevoElemento += CrearElemento;
            _model.NuevoGrafoPrefabricado();
            _model.CrearArbol();
        }


        private void BorrarArbol()
        {
            Arbol.Clear();
            ResetSeleccion();
            _model.BorrarArbol();
        }

        private void OcultarPesos()
        {
            foreach (var elementoVisual in Arbol)
            {
                if (elementoVisual is AristaVisual)
                {
                    elementoVisual.Etiqueta = "";
                }
            }
        }

        private void AñadirVertice()
        {
            _model.AñadirVertice(X, Y);
        }

        private void AñadirArista()
        {
            _model.AñadirArista();
            ResetSeleccion();

            Arbol.Clear();
            _verticesVisuales.Clear();
            _aristasVisuales.Clear();
            _model.CrearArbol();
        }

        private void AñadirVertices()
        {
            ContenidoBoton = _nuevoVertice ? "Añadir vértices" : "Terminado";
            NotifyPropertyChanged("ContenidoBoton");
            _nuevoVertice = _nuevoVertice ? false : true;
        }

        private void CrearArbol()
        {
            _model = new Model(int.Parse(NumeroVertices), int.Parse(MaximasAristas), MaxX - RadioVertice * 2, MaxY - RadioVertice * 2, RadioVertice, RadioVertice);
            _model.NuevoElemento += CrearElemento;
            _model.NuevoGrafoYWeightingFunction();
            _model.CrearArbol();
        }

        private void CrearElemento(Elemento elem, List<double> valores, string etiqueta, int indice)
        {
            switch (elem)
            {
                case Elemento.Vertice:
                    Arbol.Add(new VerticeVisual(
                        valores[0] - RadioVertice, valores[1] - RadioVertice, RadioVertice*2, RadioVertice*2, etiqueta,
                        "V" + indice, Brushes.LightGray));
                    _verticesVisuales[Arbol.Last()] = indice;
                    break;
                case Elemento.Arista:
                    Arbol.Add(new AristaVisual(valores[0], valores[1], 0, 0, valores[2] - valores[0], valores[3] - valores[1], 
                        "A"+indice, Brushes.Black, etiqueta));
                    _aristasVisuales[Arbol.Last()] = indice; 
                    break;
            }
        }

        private void SeleccionarArista()
        {
            if (PararCommand.CanExecute(this)) PararCommand.Execute(this);

            foreach (AristaVisual arista in _aristasVisuales.Keys)
            {
                arista.Color = Brushes.Black;
            }

            foreach (var elementoVisual in Arbol)
            {
                if (elementoVisual.Identificador == AristaSeleccionado)
                {
                    _model.AristaSeleccionada = _aristasVisuales[elementoVisual];
                    elementoVisual.Color = Brushes.YellowGreen;
                    break;
                }
            }
        }
        
        private void SeleccionarVertice()
        {
            if (PararCommand.CanExecute(this)) PararCommand.Execute(this);

            foreach (var elementoVisual in Arbol)
            {
                if (elementoVisual.Identificador == VerticeSeleccionado)
                {
                    if (_model.Origen == null)
                    {
                        _model.Origen = _model.Vertices[_verticesVisuales[elementoVisual]];
                        elementoVisual.Color = Brushes.Yellow;
                    }
                    else if (_model.Destino == null)
                    {
                        _model.Destino = _model.Vertices[_verticesVisuales[elementoVisual]];
                        elementoVisual.Color = Brushes.LightGreen;
                    }
                    else
                    {
                        ResetSeleccion();
                        SeleccionarVertice();
                    }
                    break;
                }
            }
        }

        public void ElementoModificado(string c, Object o)
        {
            _model.Etiquetas[o] = c;
        }

        private void ResetSeleccion()
        {
            foreach (VerticeVisual vertice in _verticesVisuales.Keys)
            {
                vertice.Color = Brushes.LightGray;
            }
            foreach (AristaVisual arista in _aristasVisuales.Keys)
            {
                arista.Color = Brushes.Black;
            }

            _model.Origen = null;
            _model.Destino = null;
            _model.AristaSeleccionada = -1;
        }

        private void EliminarElemento()
        {
            Arbol.Clear();
            _verticesVisuales.Clear();
            _aristasVisuales.Clear();
            if (_model.AristaSeleccionada >= 0)
            {
                _model.EliminarArista();
            }
            if (_model.Origen != null || _model.Destino != null)
            {
                _model.EliminarVertice();
            }
            _model.CrearArbol();
            ResetSeleccion();
        }

        private void Solucionar()
        {
            ResetSeleccion();
            var solucion = _solver.Travel(_model.Points);

            for (var i = 0; i < solucion.Count()-1; i++)
            {
                var p1 = solucion.ElementAt(i);
                var p2 = solucion.ElementAt(i+1);

                foreach (var arista in _model.Aristas)
                {
                    if (arista.Item1.X == p1.Item1 && arista.Item1.Y == p1.Item2
                        && arista.Item2.X == p2.Item1 && arista.Item2.Y == p2.Item2)
                    {
                        DestacarArista(arista);
                    }
                }
            }
        }

        private void Resolver()
        {
            ResetSeleccion();
            

        }

        private void DestacarArista(Tuple<Vertice, Vertice> arista)
        {
            for (var i = 0; i < _model.Aristas.Count; i++)
            {
                if (arista.Item1 == _model.Aristas[i].Item1 && arista.Item2 == _model.Aristas[i].Item2)
                {
                    foreach (var aristaVisual in _aristasVisuales.Keys)
                    {
                        if (_aristasVisuales[aristaVisual] == i)
                        {
                            aristaVisual.Color = Brushes.Red;
                        }
                    }
                }
            }
        }

        private void Step()
        {
            DestacarArista(_solucion.First());
            _solucion.RemoveAt(0);
        }
    }
}